package compilers.symbolTable;

import java.util.Iterator;
import java.util.LinkedList;

import compilers.AST.basics.Parameter;
import compilers.AST.basics.VariableParameter;
import compilers.AST.expression.Expression;
import compilers.AST.types.TypeSpecifier;
import compilers.exceptions.SemanticalException;

public class SymbolTableFunctionElement extends SymbolTableElement {
	
	private LinkedList<Parameter> fParameters;
	private boolean fDefinition;

	public SymbolTableFunctionElement(String identifier, TypeSpecifier returnType, LinkedList<Parameter> parameters, boolean definition, int nodeId, int lineNr) {
		fIdentifier = identifier;
		fType = returnType;
		fParameters = parameters;
		fDefinition = definition;
		fNodeId = nodeId;
		fLineNr = lineNr;
	}
	
	public void print(int identLevel) {
		String result = "Function (" + fIdentifier + ", " + fType.getTypeName();
		if (!fParameters.isEmpty()) {
			Iterator<Parameter> it = fParameters.iterator();
			while (it.hasNext()) {
				result += ", " + it.next().getSymbolTableOutput();
			}
		}
		result += ")";
		println(identLevel, result);
	}
	
	public boolean isDefinition() {
		return fDefinition;
	}
	
	public LinkedList<Parameter> getParameters() {
		return fParameters;
	}
	
	public void checkSemantics(SymbolTableFunctionElement forwardDecl, SymbolTableScope currentScope) throws SemanticalException {
		//Check return type
		if (fType.getType(currentScope) != forwardDecl.getType().getType(currentScope)) {
			throw new SemanticalException("The return type '" + fType.getTypeName() + "' of the function '" + fIdentifier + "' is not the same as the result type '" + forwardDecl.getType().getTypeName() + "' of the forward declaration at line " + forwardDecl.getLineNr() +".", fLineNr);
		}

		//Check parameters
		int nrOfParmsDef = fParameters.size();
		int nrOfParmsDecl = forwardDecl.fParameters.size();
		
		if(nrOfParmsDef < nrOfParmsDecl) {
			throw new SemanticalException("Not enough arguments given to define function '" + fIdentifier + "'.", fLineNr);
		} else if(nrOfParmsDef > nrOfParmsDecl) {
			throw new SemanticalException("Too many arguments given to define function '" + fIdentifier + "'.", fLineNr);
		} else {
			Iterator<Parameter> defParmIt = fParameters.iterator();
			Iterator<Parameter> declParmIt = forwardDecl.fParameters.iterator();
			int counter = 1;
			while(defParmIt.hasNext()) {
				Parameter defParm = defParmIt.next();		
				Parameter declParm = declParmIt.next();
				if(!defParm.compare(declParm, currentScope)) {
					throw new SemanticalException("Parameter " + counter + " of function '" + fIdentifier + "' does not match with forward declaration.", fLineNr);
				}
				counter++;
			}
		}
	}
}
